Join Order and Indexes
If you are here, then you have a SQL that is not using an index. You have already:
- Checked that the index exists
- Checked the table and index have been analysed
- Used the INDEX hint to force Oracle to use the index with a UNIQUE or RANGE scan, but failed.
If you have not checked these things, go back and do so before continuing.
Consider the following SQL
SELECT a.*
FROM table_a a, table_b b
WHERE a.col5 = :x
AND a.key = b.key
Let's assume the following:
- Table_a and table_b are large tables
- a.col5 is fairly selective (<4% of table_a per distinct value)
- b.key has high cardinality (<10 rows in table_b per distinct value)
- b.key is indexed
The best result for this SQL is to read table_a retrieving rows where col5 matches the bind variable :x (an index on a.col5 will help, but is not mandatory), then for each row use an index to lookup table_b on each b.key. So what happens if Oracle doesn't do it this way? Some possibilities are:
- The tables are Hash joined. This will usually happen when statistics are not calculated, or if a.col5 has poor selectivity.
- The tables are processed in the reverse order (table_b, then table_a). This could happen if the index on b.key is missing, but there is an index on a.key.
If statistics on the tables are up to date and we are determined to use the index on table_b, then we must do two things to counteract the Cost Based Optimizer:
- Add an ORDERED hint to force the join order.
- Add a USE_NL hint to force the use of a Nested Loops join.
- Add an INDEX hint to force the use of an index.
SELECT /*+ ORDERED USE_NL(b) INDEX(b, table_b_i1)*/ a.*
FROM table_a a, table_b b
WHERE a.col5 = :x
AND a.key = b.key
Check the execution plan with Explain Plan.
- Is it joining the tables in the correct order?
If not, check the spelling of your ORDERED hint. Also ensure that the join is not an Outer Join; Outer Joins must start with the inner table and join to the outer table.
- Is it using a Nested Loops join?
If not, check your USE_NL hint. You must use the table alias in the hint, not the table name (unless you have not used an alias).
- If the join order and join method are correct, is it using a different index on table_b? Make sure you spelled the index name correctly in the INDEX hint.
Is it using the index now? If not, then the index is somehow unusable - see your DBA. Even if it is now using the index, the Cost Based Optimiser thinks there is a better way. Do it the courtesy of comparing the SQL with and without hints.